#include <stdio.h>
#include "esp_log.h"
#include "mqtt_protocol.h"
#include "mymqtt.h"
#include "config.h"
#include "https_download2.h"
#include "system_info.h"
#include "utils.h"

#define TAG "mq"
uint32_t last_free;

void cmd_reboot(DynamicJsonDocument &root, MqttReplayMessage &replay);
void cmd_download(DynamicJsonDocument &root, MqttReplayMessage &replay);
void cmd_settings(DynamicJsonDocument &root, MqttReplayMessage &replay);
void cmd_info(DynamicJsonDocument &root, MqttReplayMessage &replay);
void cmd_tasks(DynamicJsonDocument &root, MqttReplayMessage &replay);
void cmd_settings(DynamicJsonDocument &root, MqttReplayMessage &replay);
void cmd_err(MqttReplayMessage &replay);

void mqtt_publish(esp_mqtt_client_handle_t client, MqttBaseMessage &msg)
{
    int msg_id = esp_mqtt_client_publish(client, msg.topic.c_str(), msg.payload.c_str(), msg.payload.length(), 0, 0);
}

void on_topic_msg_down(esp_mqtt_event_handle_t event)
{
    MqttReplayMessage replay = MqttReplayMessage();
    String cmd = "";
    String opt = "";
    DynamicJsonDocument root(4096);
    DeserializationError err;

    char *payload = (char *)malloc(event->data_len + 1);
    if (payload == nullptr)
    {
        ESP_LOGE(TAG, "Memory allocation failed");
        replay.err = -1;
        replay.msg = "Memory allocation failed";
        goto end;
    }

    memset(payload, 0, event->data_len + 1);
    memcpy(payload, event->data, event->data_len);
    // ESP_LOGI(TAG, "payload len:%d",event->data_len);
    // ESP_LOGI(TAG, "payload :%s",payload);

    err = deserializeJson(root, payload);
    if (err)
    {
        log_e( "deserializeJson(root,payload) failed:%s", err.c_str());
        ESP_LOGE(TAG, "deserializeJson(root,payload) failed:%s", err.c_str());
        char buf[128];
        snprintf(buf, 128, "data len:%d;\n deserializeJson err:%s;", event->data_len, err.c_str());
        replay.err = -1;
        replay.msg = buf;
        goto end;
    }

    if (!root["data"]["cmd"].isNull())
    {
        cmd = root["data"]["cmd"].as<String>();
        log_i("cmd:%s", cmd.c_str());
        ESP_LOGI(TAG,"cmd:%s", cmd.c_str());
    }
    else
    {
        log_e("cmd is null");
        replay.err = -2;
        replay.msg = "cmd is null;";
        goto end;
    }
    if (!root["data"]["opt"].isNull())
    {
        opt = root["data"]["opt"].as<String>();
        log_i("opt:%s", opt.c_str());
        ESP_LOGI(TAG,"opt:%s", opt.c_str());
    }
    else
    {
        log_e( "opt is null");
        replay.err = -3;
        replay.msg = "opt is null;";
        goto end;
    }

    if (cmd == "reboot")
    {
        cmd_reboot(root, replay);
    }
    else if (cmd == "download")
    {
        cmd_download(root, replay);
    }
    else if (cmd == "info")
    {
        cmd_info(root, replay);
    }
    else if (cmd == "tasks")
    {
        cmd_tasks(root, replay);
    }
    else if (cmd == "settings")
    {
        cmd_settings(root, replay);
    }
end:
    free(payload);
    if (replay.err != 0)
    {
        cmd_err(replay);
    }
    mqtt_publish(event->client, replay);

    uint32_t free_now = esp_get_free_heap_size();
    int32_t diff = last_free - free_now;
    ESP_LOGI(TAG, "%lu,%ld", free_now, diff);
    last_free = free_now;
    return;
}

void cmd_err(MqttReplayMessage &replay)
{
    char buf[2048];

    DynamicJsonDocument data(2048);
    replay.topic = MqttTopicsUp;
    replay.id = "000";
    data["cmd"] = "unkown";
    data["opt"] = "ack";

    replay.JsonObj(data);
    return;
}
void cmd_info(DynamicJsonDocument &root, MqttReplayMessage &replay)
{
    DynamicJsonDocument data(512);
    replay.topic = MqttTopicsUp;
    replay.id = root["id"].as<String>();
    data["cmd"] = "info";
    data["opt"] = "ack";
    replay.err = 0;
    data["hardware"] = SystemInfo::GetChipModelName();
    data["software"] = "xxxxx";
    data["FreeHeap"] = SystemInfo::GetFreeHeapSize();
    data["MiniFreeHeap"] = SystemInfo::GetMinimumFreeHeapSize();
    data["Flash"] = SystemInfo::GetFlashSize();
    data["Mac"] = SystemInfo::GetMacAddress();

    replay.JsonObj(data);
    return;
}

void cmd_tasks(DynamicJsonDocument &root, MqttReplayMessage &replay)
{
    char buf[2048];

    DynamicJsonDocument data(2048);
    replay.topic = MqttTopicsUp;
    replay.id = root["id"].as<String>();
    data["cmd"] = "tasks";
    data["opt"] = "ack";
    replay.err = 0;

    memset(buf, 0, 2048);
    SystemInfo::PrintRealTimeStatsToBuffer(buf, 2048, 2000);
    data["tasks"] = buf;
    printf("%s\n", buf);

    replay.JsonObj(data);
    return;
}
void cmd_reboot(DynamicJsonDocument &root, MqttReplayMessage &replay)
{
    DynamicJsonDocument data(512);
    replay.topic = MqttTopicsUp;
    replay.id = root["id"].as<String>();
    data["cmd"] = "reboot";
    data["opt"] = "ack";
    replay.JsonObj(data);
    return;
}

char mqtt_https_cert[2048];

HttpsDownloadInfo_t info;

void cmd_download(DynamicJsonDocument &root, MqttReplayMessage &replay)
{
    DynamicJsonDocument data(512);
    replay.topic = MqttTopicsUp;
    replay.id = root["id"].as<String>();
    data["cmd"] = "download";
    data["opt"] = "ack";
    String url;
    String path;
    replay.err = 0;

    if (!root["data"]["url"].isNull())
    {
        url = root["data"]["url"].as<String>();
        data["url"] = url;
        ESP_LOGI(TAG, "url:%s", url.c_str());
    }
    else
    {
        replay.err = -1;
        data["err"] = -1;
        goto end;
    }

    // 可选参数
    if (!root["data"]["path"].isNull())
    {
        path = root["data"]["path"].as<String>();
        ESP_LOGI(TAG, "path:%s", path.c_str());
    }
    else
    {
        // 如果 info.filePath 为空，则将 info.url 的最后一个 / 后的文件名作为 filePath
        int lastSlashIndex = url.lastIndexOf('/');
        if (lastSlashIndex != -1)
        {
            path = "/" + url.substring(lastSlashIndex + 1);
        }

        ESP_LOGI(TAG, "path = %s", path.c_str());
    }
    data["filepath"] = path;

    if (replay.err == 0)
    {
        memset(mqtt_https_cert, 0, 2048);
        memcpy(mqtt_https_cert, config.get_https_cert().c_str(), config.get_https_cert().length());

        info.url = url;
        info.filePath = path;
        info.certPem = mqtt_https_cert;

        // 任务名称最长16字节
        downloader.add(info);
    }
end:
    replay.JsonObj(data);
    return;
}

void cmd_settings(DynamicJsonDocument &root, MqttReplayMessage &replay)
{
    DynamicJsonDocument data(2048);
    replay.topic = MqttTopicsUp;
    replay.id = root["id"].as<String>();
    data["cmd"] = "settings";
    data["opt"] = "ack";
    if (root["data"]["opt"] == "set")
    {
        if (!root["data"]["module_name"].isNull())
        {
            String module_name = root["data"]["module_name"].as<String>();
            config.set_module_name(module_name);
            ESP_LOGI(TAG, "get_module_name:%s", config.get_module_name().c_str());
            data["module_name"] = config.get_module_name();
        }
        if (!root["data"]["dev_name"].isNull())
        {
            String dev_name = root["data"]["dev_name"].as<String>();
            config.set_dev_name(dev_name);
            ESP_LOGI(TAG, "dev_name:%s", config.get_dev_name().c_str());
            data["dev_name"] = config.get_dev_name();
        }
        if (!root["data"]["dev_pwd1"].isNull())
        {
            String value = root["data"]["dev_pwd1"];
            ESP_LOGI(TAG, "dev_pwd1:%s", value.c_str());
            config.set_dev_pwd1(value);
            data["dev_pwd1"] = config.get_dev_pwd1();
        }
        if (!root["data"]["dev_pwd2"].isNull())
        {
            String value = root["data"]["dev_pwd2"];
            ESP_LOGI(TAG, "dev_pwd2:%s", value.c_str());
            config.set_dev_pwd2(value);

            data["dev_pwd2"] = config.get_dev_pwd2();
        }
        if (!root["data"]["mqtt_en"].isNull())
        {
            uint16_t mqtt_en = root["data"]["mqtt_en"].as<int>();
            config.set_mqtt_en(mqtt_en);
            ESP_LOGI(TAG, "mqtt_en:%d", config.get_mqtt_en());
            data["mqtt_en"] = config.get_mqtt_en();
        }

        if (!root["data"]["mqtt_interval"].isNull())
        {
            uint16_t mqtt_interval = root["data"]["mqtt_interval"].as<int>();
            config.set_mqtt_interval(mqtt_interval);
            ESP_LOGI(TAG, "mqtt_interval:%d", config.get_mqtt_interval());
            data["mqtt_interval"] = config.get_mqtt_interval();
        }

        if (!root["data"]["mqtt_url"].isNull())
        {
            String mqtt_url = root["data"]["mqtt_url"].as<String>();
            config.set_mqtt_url(mqtt_url);
            ESP_LOGI(TAG, "mqtt_url:%s", config.get_mqtt_url().c_str());
            data["mqtt_url"] = config.get_mqtt_url();
        }
        if (!root["data"]["mqtt_name"].isNull())
        {
            String value = root["data"]["mqtt_name"];
            config.set_mqtt_name(value);
            ESP_LOGI(TAG, "mqtt_name:%s", config.get_mqtt_name().c_str());
            data["mqtt_name"] = config.get_mqtt_name();
        }
        if (!root["data"]["mqtt_pwd"].isNull())
        {
            String value = root["data"]["mqtt_pwd"];
            config.set_mqtt_pwd(value);
            ESP_LOGI(TAG, "mqtt_pwd:%s", config.get_mqtt_pwd().c_str());
            data["mqtt_pwd"] = config.get_mqtt_pwd();
        }
        if (!root["data"]["mqtt_cert"].isNull())
        {
            String value = root["data"]["mqtt_cert"];
            config.set_mqtt_cert(value);
            ESP_LOGI(TAG, "mqtt_cert:%s", config.get_mqtt_cert().c_str());
            data["mqtt_cert"] = config.get_mqtt_cert();
        }

        if (!root["data"]["wifi_ssid"].isNull())
        {
            String value = root["data"]["wifi_ssid"];
            config.set_wifi_ssid(value);
            ESP_LOGI(TAG, "wifi_ssid:%s", config.get_wifi_ssid().c_str());
            data["wifi_ssid"] = config.get_wifi_ssid();
        }
        if (!root["data"]["wifi_pwd"].isNull())
        {
            String value = root["data"]["wifi_pwd"];
            config.set_wifi_pwd(value);
            ESP_LOGI(TAG, "wifi_pwd:%s", config.get_wifi_pwd().c_str());
            data["wifi_pwd"] = config.get_wifi_pwd();
        }
        if (!root["data"]["wifi_mode"].isNull())
        {
            uint16_t value = root["data"]["wifi_mode"];
            config.set_wifi_mode(value);
            ESP_LOGI(TAG, "wifi_mode:%d", config.get_wifi_mode());
            data["wifi_mode"] = config.get_wifi_mode();
        }
        if (!root["data"]["wifi_ip"].isNull())
        {
            String value = root["data"]["wifi_ip"];
            IPAddress ip;
            if (ip.fromString(value))
            {
                config.set_wifi_ip(ip);
                ESP_LOGI(TAG, "wifi_ip:%s", config.get_wifi_ip().toString().c_str());
            }
            else
            {
                replay.err += -1;
                replay.msg += "[wifi_ip][ip:" + value + "] err";
            }
            data["wifi_ip"] = config.get_wifi_ip();
        }
        if (!root["data"]["wifi_subnet"].isNull())
        {
            String value = root["data"]["wifi_subnet"];
            IPAddress ip;
            if (ip.fromString(value))
            {
                config.set_wifi_subnet(ip);
                ESP_LOGI(TAG, "wifi_subnet:%s", config.get_wifi_subnet().toString().c_str());
            }
            else
            {
                replay.err += -1;
                replay.msg += "[wifi_subnet][ip:" + value + "] err";
            }
            data["wifi_subnet"] = config.get_wifi_subnet();
        }
        if (!root["data"]["wifi_gw"].isNull())
        {
            String value = root["data"]["wifi_gw"];
            IPAddress ip;
            if (ip.fromString(value))
            {
                config.set_wifi_gw(ip);
                ESP_LOGI(TAG, "wifi_gw:%s", config.get_wifi_gw().toString().c_str());
            }
            else
            {
                replay.err += -1;
                replay.msg += "[wifi_gw][ip:" + value + "] err";
            }
            data["wifi_gw"] = config.get_wifi_gw();
        }
        if (!root["data"]["dns0"].isNull())
        {
            String value = root["data"]["dns0"];
            IPAddress ip;
            if (ip.fromString(value))
            {
                config.set_dns(0, ip);
                ESP_LOGI(TAG, "wifi_gw:%s", config.get_dns(0).toString().c_str());
            }
            else
            {
                replay.err += -1;
                replay.msg += "[dns0][ip:" + value + "] err";
            }
            data["dns0"] = config.get_dns(0);
        }
        if (!root["data"]["dns1"].isNull())
        {
            String value = root["data"]["dns1"];
            IPAddress ip;
            if (ip.fromString(value))
            {
                config.set_dns(1, ip);
                ESP_LOGI(TAG, "dns1:%s", config.get_dns(1).toString().c_str());
            }
            else
            {
                replay.err += -1;
                replay.msg += "[dns1][ip:" + value + "] err";
            }
            data["dns1"] = config.get_dns(1);
        }
    }
    if (root["data"]["opt"] == "get")
    {
        data["module_name"] = config.get_module_name();
        data["dev_name"] = config.get_dev_name();
        data["dev_pwd1"] = config.get_dev_pwd1();
        data["dev_pwd2"] = config.get_dev_pwd2();
        data["mqtt_en"] = config.get_mqtt_en();
        data["mqtt_interval"] = config.get_mqtt_interval();
        data["mqtt_url"] = config.get_mqtt_url();
        data["mqtt_name"] = config.get_mqtt_name();
        data["mqtt_pwd"] = config.get_mqtt_pwd();
        data["wifi_ssid"] = config.get_wifi_ssid();
        data["wifi_pwd"] = config.get_wifi_pwd();
        data["wifi_mode"] = config.get_wifi_mode();
        data["wifi_ip"] = config.get_wifi_ip();
        data["wifi_subnet"] = config.get_wifi_subnet();
        data["wifi_gw"] = config.get_wifi_gw();
        data["dns0"] = config.get_dns(0);
        data["dns1"] = config.get_dns(1);
        data["mqtt_cert"] = config.get_mqtt_cert();
    }

    data["msg"] = replay.msg;
    data["err"] = replay.err;

    replay.JsonObj(data);
    return;
}

void cmd_settings_mqtt(DynamicJsonDocument &root, MqttReplayMessage &replay)
{
    DynamicJsonDocument data(512);
    replay.topic = MqttTopicsUp;
    replay.id = root["id"].as<String>();
    data["cmd"] = "settings";
    data["opt"] = "ack";
    if (root["data"]["opt"] == "set")
    {
    }

    replay.JsonObj(data);
    return;
}